#!/usr/bin/env python
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Utility to show outstanding reviews for the project

Usage: list-missing-shipits [--server=RB_SERVER_URL]
"""
from __future__ import print_function

import datetime
import getpass
import json
import os
import subprocess
import sys

from optparse import OptionParser


class Review(object):
  def __init__(self, raw_review):
    self._review = raw_review

  @property
  def shipit(self):
    return self._review['ship_it']

  @property
  def timestamp(self):
    return self._review['timestamp']

  @property
  def reviewer(self):
    return self._review['links']['user']['title']


class Diff(object):
  def __init__(self, raw_diff):
    self._diff = raw_diff

  @property
  def timestamp(self):
    return self._diff['timestamp']


class ReviewRequest(object):
  SUMMARY_TRUNCATE = 20

  def __init__(self, raw_request):
    self._request = raw_request

  @property
  def submitter(self):
    return self._request['links']['submitter']['title']

  @property
  def ships_required(self):
    return [p['title'] for p in self._request['target_people']]

  @property
  def id(self):
    return self._request['id']

  @property
  def summary(self):
    full = self._request['summary']
    return ((full[:(self.SUMMARY_TRUNCATE - 3)] + '...')
            if len(full) > self.SUMMARY_TRUNCATE else full)

  @property
  def reviews_href(self):
    return self._request['links']['reviews']['href']

  @property
  def diffs_href(self):
    return self._request['links']['diffs']['href']


class Reviewboard(object):
  def __init__(self, url=None):
    self._path = os.path.realpath(os.path.join(os.path.dirname(sys.argv[0]), '..', '..', 'rbt'))
    self._url = url

  def _get(self, resource, params={}):
    base_cmd = [self._path, 'api-get']
    if self._url is not None:
      base_cmd += ['--server=%s' % self._url]
    return json.loads(subprocess.Popen(
        base_cmd + [resource] + ['--%s=%s' % (k, v) for k, v in params.items()],
        stdout=subprocess.PIPE).communicate()[0])

  def get_server_url(self):
    return self._get('info')['info']['site']['url']

  def get_review_requests(self, params):
    return [ReviewRequest(r) for r in self._get('review-requests', params)['review_requests']]

  def get_reviews(self, review_request):
    return [Review(r) for r in self._get(review_request.reviews_href)['reviews']]

  def get_diffs(self, review_request):
    return [Diff(d) for d in self._get(review_request.diffs_href)['diffs']]


def main():
  parser = OptionParser()
  parser.add_option('--server',
                    dest='server',
                    help='ReviewBoard server',
                    default=None)
  (options, args) = parser.parse_args()

  api = Reviewboard(options.server)

  server_url = api.get_server_url()
  def request_url(id):
    return '%sr/%s' % (server_url, id)

  pending_requests = api.get_review_requests({
      'to-groups': 'Aurora',
      'status': 'pending',
    })

  def to_row(request):
    reviews = api.get_reviews(request)
    diffs = api.get_diffs(request)
    ships_required = set(request.ships_required)
    for event in sorted(reviews + diffs, key=lambda x: x.timestamp, reverse=True):
      if isinstance(event, Review):
        if event.shipit:
          # A 'ship-it' review leaves remaining reviewers on the hook.
          ships_required.discard(event.reviewer)
          continue
        elif event.reviewer in ships_required:
          waiting_for = 'awaiting updated diff'
          break
      else:
        ships = [r.reviewer for r in reviews if r.shipit]
        needed = set(request.ships_required) - set(ships)
        if not request.ships_required:
          waiting_for = 'no reviewers specified'
        elif not needed:
          waiting_for = 'ready to submit'
        else:
          waiting_for = 'need review from %s' % ', '.join(needed)
        break

    return (str(event.timestamp),
            request_url(request.id),
            request.submitter.ljust(10),
            request.summary.ljust(ReviewRequest.SUMMARY_TRUNCATE),
            waiting_for)

  table = [to_row(r) for r in pending_requests]
  sorted_table = sorted(table, key=lambda row: row[0])
  print('\n'.join('\t'.join(row) for row in sorted_table))

  print()

  recently_submitted_days = 1
  updated_from = ((datetime.datetime.utcnow() - datetime.timedelta(days=1))
                  .replace(microsecond=0).isoformat())
  recently_submitted = api.get_review_requests({
      'to-groups': 'Aurora',
      'status': 'submitted',
      'last-updated-from': updated_from
    })
  print('Recently submitted: %s' % len(recently_submitted))
  for request in recently_submitted:
    print('\t'.join((request_url(request.id), request.submitter, request.summary)))


if __name__ == '__main__':
  main()
